/*
 * @Author: ljk
 * @Date: 2023-07-26 17:41:48
 * @LastEditors: ljk
 * @LastEditTime: 2023-07-27 10:20:19
 * @Description: CP问题: 环形队列
 */
#pragma once
#include <iostream>
#include <pthread.h>
#include <time.h>
#include <stdlib.h>
#include <semaphore.h>
#include <cstring>
#include <unistd.h>
#include <sys/types.h>
#include <memory>
#include <vector>

static const int N = 5;

template<class T>
class RingQueue
{
private:
    void P(sem_t &s)
    {
        sem_wait(&s);
    }
    void V(sem_t &s)
    {
        sem_post(&s);
    }
    void Lock(pthread_mutex_t& m)
    {
        pthread_mutex_lock(&m);
    }
    void Unlock(pthread_mutex_t& m)
    {
        pthread_mutex_unlock(&m);
    }
public:
    RingQueue(int num = N): _ring(num), _cap(num)
    {
        sem_init(&_data_sem, 0, 0);
        sem_init(&_space_sem, 0, num);
        _p_step = _c_step = 0;

        pthread_mutex_init(&_c_mutex, nullptr);
        pthread_mutex_init(&_p_mutex, nullptr);
    }
    void push(const T& in)
    {
        //为什么推荐先申请信号量呢?
        //当先申请锁时, 意味着其它线程只能在外部等待, 等CPU分配到为锁解开
        //才开始申请对应的资源,这样子无形中降低了效率
        //事实上我们可以先申请信号量, 将资源分配到位, 而后直接锁解开直接运行提升了效率
        //好比教室外分配座位的例子
        //生产

        //信号量的意义
        //1.可以不用在临界区内部做判断，就可以知道临界区资源的使用情况
        //2.什么时候用锁，什么时候用信号量? 取决于对应的临界资源,是否被整体使用
        P(_space_sem);//P(space)
        Lock(_p_mutex);

        //此处表明一定有对应的资源(信号量申请成功)
        _ring[_p_step++] = in;
        _p_step %= _cap;

        Unlock(_p_mutex);
        V(_data_sem);//V(data)
    }
    void pop(T* out)
    {
        //消费
        P(_data_sem);
        Lock(_c_mutex);

        //消费哪一个
        *out = _ring[_c_step++];
        _c_step %= _cap;

        Unlock(_c_mutex);
        V(_space_sem);
    }
    ~RingQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_space_sem);

        pthread_mutex_destroy(&_c_mutex);
        pthread_mutex_destroy(&_p_mutex);
    }
private:
    std::vector<T> _ring; //环形队列
    int _cap; //环形队列的容量
    //sem_t维护的是一段联合体由OS维护
    sem_t _data_sem; //数据信号量(消费者关心)
    sem_t _space_sem; //空间信号量(生产者关心)
    //要根据生产下标跟消费下标来判断状态
    int _c_step; //消费位置
    int _p_step; //生产位置

    pthread_mutex_t _c_mutex;
    pthread_mutex_t _p_mutex;
};
